{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "<a href=\"https://githubtocolab.com/gee-community/geemap/blob/master/docs/notebooks/37_pydeck_3d.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open in Colab\"/></a>\n",
    "\n",
    "Uncomment the following line to install [geemap](https://geemap.org) if needed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install geemap"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2",
   "metadata": {},
   "source": [
    "#  How to use Earth Engine with pydeck for 3D terrain visualization\n",
    "Pydeck + Earth Engine: Terrain Visualization\n",
    "\n",
    "**Requirements**\n",
    "\n",
    "- [earthengine-api](https://github.com/google/earthengine-api): a Python client library for calling the Google Earth Engine API.\n",
    "- [pydeck](https://pydeck.gl/index.html): a WebGL-powered framework for visual exploratory data analysis of large datasets.\n",
    "- [pydeck-earthengine-layers](https://github.com/UnfoldedInc/earthengine-layers/tree/master/py): a pydekc wrapper for Google Earth Engine. For documentation please visit this [website](https://earthengine-layers.com/).\n",
    "- [Mapbox API key](https://pydeck.gl/installation.html#getting-a-mapbox-api-key): you will need this add basemap tiles to pydeck.\n",
    "\n",
    "**Installation**\n",
    "\n",
    "- conda create -n deck python\n",
    "- conda activate deck\n",
    "- conda install mamba -c conda-forge\n",
    "- mamba install earthengine-api pydeck pydeck-earthengine-layers -c conda-forge\n",
    "- jupyter nbextension install --sys-prefix --symlink --overwrite --py pydeck\n",
    "- jupyter nbextension enable --sys-prefix --py pydeck"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    "This example is adopted from [here](https://github.com/UnfoldedInc/earthengine-layers/blob/master/py/examples/terrain.ipynb). Credits to the developers of the [pydeck-earthengine-layers](https://github.com/UnfoldedInc/earthengine-layers) package."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4",
   "metadata": {},
   "source": [
    "## 2D Visualization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import ee\n",
    "import geemap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "Map = geemap.Map()\n",
    "Map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7",
   "metadata": {},
   "outputs": [],
   "source": [
    "image = ee.Image(\"USGS/NED\").select(\"elevation\")\n",
    "vis_params = {\n",
    "    \"min\": 0,\n",
    "    \"max\": 4000,\n",
    "    \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n",
    "}\n",
    "Map.addLayer(image, vis_params, \"NED\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8",
   "metadata": {},
   "source": [
    "## 3D Visualization\n",
    "\n",
    "### Import libraries"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "First, import required packages. Note that here we import the `EarthEngineTerrainLayer` instead of the `EarthEngineLayer`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "import ee\n",
    "import pydeck as pdk\n",
    "from pydeck_earthengine_layers import EarthEngineTerrainLayer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11",
   "metadata": {},
   "source": [
    "### Authenticate with Earth Engine\n",
    "\n",
    "Using Earth Engine requires authentication. If you don't have a Google account approved for use with Earth Engine, you'll need to request access. For more information and to sign up, go to https://signup.earthengine.google.com/."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    ee.Initialize()\n",
    "except Exception as e:\n",
    "    ee.Authenticate()\n",
    "    ee.Initialize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13",
   "metadata": {},
   "source": [
    "### Terrain Example\n",
    "\n",
    "In contrast to the `EarthEngineLayer`, where you need to supply _one_ Earth Engine object to render, with the `EarthEngineTerrainLayer` you must supply **two** Earth Engine objects. The first is used to render the image in the same way as the `EarthEngineLayer`; the second supplies elevation values used to extrude terrain in 3D. Hence the former can be any `Image` object; the latter must be an `Image` object whose values represents terrain heights.\n",
    "\n",
    "It's important for the terrain source to have relatively high spatial resolution. In previous examples, we used [SRTM90][srtm90] as an elevation source, but that only has a resolution of 90 meters. When used as an elevation source, it looks very blocky/pixelated at high zoom levels. In this example we'll use [SRTM30][srtm30] (30-meter resolution) as the `Image` source and the [USGS's National Elevation Dataset][ned] (10-meter resolution, U.S. only) as the terrain source. SRTM30 is generally the best-resolution worldwide data source available.\n",
    "\n",
    "[srtm90]: https://developers.google.com/earth-engine/datasets/catalog/CGIAR_SRTM90_V4\n",
    "[srtm30]: https://developers.google.com/earth-engine/datasets/catalog/USGS_SRTMGL1_003\n",
    "[ned]: https://developers.google.com/earth-engine/datasets/catalog/USGS_NED"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "# image = ee.Image('USGS/SRTMGL1_003')\n",
    "image = ee.Image(\"USGS/NED\").select(\"elevation\")\n",
    "terrain = ee.Image(\"USGS/NED\").select(\"elevation\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15",
   "metadata": {},
   "source": [
    "Here `vis_params` consists of parameters that will be passed to the Earth Engine [`visParams` argument][visparams]. Any parameters that you could pass directly to Earth Engine in the code editor, you can also pass here to the `EarthEngineLayer`.\n",
    "\n",
    "[visparams]: https://developers.google.com/earth-engine/image_visualization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {},
   "outputs": [],
   "source": [
    "vis_params = {\n",
    "    \"min\": 0,\n",
    "    \"max\": 4000,\n",
    "    \"palette\": [\"006633\", \"E5FFCC\", \"662A00\", \"D8D8D8\", \"F5F5F5\"],\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17",
   "metadata": {},
   "source": [
    "Now we're ready to create the Pydeck layer. The `EarthEngineLayer` makes this simple. Just pass the Earth Engine object to the class."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18",
   "metadata": {},
   "source": [
    "Including the `id` argument isn't necessary when you only have one pydeck layer, but it is necessary to distinguish multiple layers, so it's good to get into the habit of including an `id` parameter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "ee_layer = EarthEngineTerrainLayer(image, terrain, vis_params, id=\"EETerrainLayer\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20",
   "metadata": {},
   "source": [
    "Then just pass this layer to a `pydeck.Deck` instance, and call `.show()` to create a map:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21",
   "metadata": {},
   "outputs": [],
   "source": [
    "view_state = pdk.ViewState(\n",
    "    latitude=36.15, longitude=-111.96, zoom=10.5, bearing=-66.16, pitch=60\n",
    ")\n",
    "\n",
    "r = pdk.Deck(layers=[ee_layer], initial_view_state=view_state)\n",
    "\n",
    "r.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
